import Sysaccess

import gtk

import gobject

import PygtkThreads
import Exceptions

import Config
import Locations
import Shares
import Mtab

import Dialog
import UIManager
import MainWindow
import LocationWindow
import ShareWindow
import XmlWindow

class Controller(gobject.GObject):
    '''The Controller class concentrate all the logic of the program
    leaving the other classes to only implement the GUI.
    All the classes with GUI should receive a Controller instance to
    be able to do things.'''

    def __init__(self,user_home_path=False):
        '''Constructor'''
        gobject.GObject.__init__(self)
        
        # Datastores
        self.config = Config.Config(user_home_path)
        
        self.locations = Locations.Locations(self)
        self.shares = Shares.Shares(self)
        self.load()
        self.mtab = Mtab.Mtab(self)
        
        # Connect locations callbacks
        self.locations.connect('add',self.add_location_callback)
        self.locations.connect('set',self.set_location_callback)
        self.locations.connect('remove',self.remove_location_callback)

        # GUI
        self.windows = []
        self.uimanager = UIManager.UIManager(self)
        self.menubar = self.uimanager.get_widget('/menubar')
        self.toolbar = self.uimanager.get_widget('/toolbar')
        self.trayicon = UIManager.TrayIcon(self)
        
        if not self.config.user['START HIDE']:
            self.main_window = MainWindow.MainWindow(self)
        else:
            self.main_window = None
        
        # Threads
        self.config.connect('set::NETWORK::STATE',self.network_state_callback)
        self.config.connect('set::NETWORK::INTERFACE',self.interface_callback)
        self.config.connect("set::USER::STARTUP START",self.set_startup_callback)
        self.config.connect('set::NETWORK::PUBLIC ADDR',self.remote_addr_changed_callback)
        self.threads = PygtkThreads.PygtkThreads(self)
        self.threads.connect('start',self.thread_start)
        self.threads.connect('end',self.thread_end)
        self.threads.start_thread('MTAB')
        self.threads.start_thread('NETWORK STATE')
        self.threads.start_thread('AUTOMOUNT')
        self.main()
    
    def test(self,*args):
        pass
        
    def load(self):
        try:
            self.locations.load()
            self.shares.load()
        except Exceptions.PickleLoadException, e :
            print "load failed fallback locations and shares to defaults"
            self.locations.load_defaults()
            self.shares.load_defaults()
            
    def get_current_location(self,location = None):
        if self.config.network['STATE']:
            public_addr = self.config.network['PUBLIC ADDR']
            if not location:
                for location_id,location in self.locations:
                    if self.get_current_location(location): # recursive call
                        return True
                self.config.network['CURRENT LOCATION'] = self.locations[0]
            else:
                for ip in location.public_addr:
                    if ip == public_addr:
                        self.config.network['CURRENT LOCATION'] = location
                        return True
                for dns in location.dns:
                    if Sysaccess.get_addr_by_name(dns) == public_addr:
                        self.config.network['CURRENT LOCATION'] = location
                        return True
                for ip in location.local_addr:
                    if ip == self.config.network['LOCAL ADDR']:
                        print "local ip match " + location.name
                        #self.config.network['CURRENT LOCATION'] = location
                
    def xml_import(self):
        XmlWindow.XmlDataTree(self,'Import')
    
    def xml_export(self):
        XmlWindow.XmlDataTree(self,'Export')
    
    #Location management method
    def add_location(self):
        '''call the location window'''
        LocationWindow.LocationWindow(self)
    
    def edit_location(self,location):
        '''call the location window with the gived location'''
        LocationWindow.LocationWindow(self,location)
        
    def remove_location(self,location):
        '''remove the gived location from datastore'''
        for share in self.shares.get_shares_by_location(location):
            self.remove_share(share)
        del self.locations[location.id]
    
    #Share management method
    def add_share(self):
        '''call the share window'''
        ShareWindow.ShareWindow(self)
        
    def edit_share(self,share):
        '''call the share window with the gived share'''
        ShareWindow.ShareWindow(self,share)
    
    def remove_share(self,share):
        '''remove the gived share from datastore'''
        if not share.mount_point.startswith(self.config.user['DEFAULT MOUNT POINT']):
            dial = Dialog.Dialog('Share Already Mounted !', "Share is mounted on " + mount_point + "\ndo you want to remount it on" + share.mount_point + " ? ",self.main_window).run()
            if dial == gtk.RESPONSE_ACCEPT:
                Sysaccess.remove_directory(share.mount_point)
        else:
            Sysaccess.remove_directory(share.mount_point)
        del self.shares[share.id]
        
    def mount_share(self,share,step=1): 
        init_step = step
        self.mtab.refresh()
        
        try:    
            if step == 1:
                self.threads.launch('LATENCY',share,True)
                if not isinstance(share.tmp, int) :
                    raise Exceptions.ConnexionFailed(share)
                step += 1

            if step == 2:
                if share.mounted:
                    raise Exceptions.ShareAlreadyMountedException(share)
                step += 1
            
            if step == 3:
                if share.mount_point in self.mtab.used_mount_points:
                    raise Exceptions.MountPointAlreadyInUseException(share)
                step += 1
            
            if step == 4:
                Sysaccess.check_mount_point(share.mount_point)
                step += 1
            
            if step == 5:
                self.threads.launch('MOUNT',share)
                
        except Exceptions.ConnexionFailed, e:
            dial = Dialog.Dialog("Server unreacheable !", share.server + " is unreacheable. Retry ?", self.main_window ).run()
                
        except Exceptions.ShareAlreadyMountedException, e :
            mount_point = self.mtab.get_current_mount_point(share)
            if not mount_point == share.mount_point: 
                dial = Dialog.Dialog('Share Already Mounted !', "Share is mounted on " + mount_point + "\ndo you want to remount it on" + share.mount_point + " ? ",self.main_window).run()
                if dial == gtk.RESPONSE_ACCEPT:
                    self.thread.launch('UNMOUNT',share)
                    if share.mounted:
                        step = -1
                else :
                    step = -1
                    
            else:
                dial = Dialog.InfoDialog('Share Already Mounted !', "Share is mounted on " + mount_point ,self.main_window).run()
                step = -1
                
        except Exceptions.MountPointAlreadyInUseException, e:
            current_share = self.mtab.get_current_share(share.mount_point)
            if current_share:
                dial = Dialog.Dialog('Mount Point Already In Use !' ," mount_point "+ share.mount_point +" is already used by " + current_share.name + "\n do you want to unmount "+current_share.name+ " and mount "+share.name,self.main_window ).run()
                if dial == gtk.RESPONSE_ACCEPT:
                    self.unmount(current_share)
                    if current_share.mounted:
                        step = -1
                else:        
                    step = -1
            else:
                pass
            
        except Exceptions.DirNotFoundException, e :
            dial = Dialog.Dialog('Mount Point doesn\'t exist !',e.path + " not found ! \n Do you want to create it with all needed parent direcory ?",self.main_window ).run()
            if dial == gtk.RESPONSE_ACCEPT:
                try:
                    Sysaccess.create_directory(e.path)
                except:
                    step = -1
            else:
                step = -1
                
        except Exceptions.DirIsNotEmptyException, e :
            dial = Dialog.InputDialog('Mount Point is not empty !', "Do you want to specify another mount point ?",self.main_window ).run()
            if dial[0] == gtk.RESPONSE_ACCEPT:
                try:
                    Sysaccess.check_mount_point(dial[1])
                    share.mount_point = dial[1]
                    step -= 1
                except:
                    step = -1
            else:        
                step = -1
            
        finally:
            if not step == 5 and not step == init_step:
                self.mount_share(share, step)
            
    def test_share(self,share):
        Sysaccess.create_directory(share.mount_point)
        mtab = [ item[1].rstrip('/') for item in Sysaccess.get_mtab()]
        if '/tmp/miam/try' in mtab:
            Sysaccess.umount('/tmp/miam/try')
        if '/tmp/miam/try' in mtab:
            raise Exception()
        
        share.tmp = Sysaccess.get_host_latency(share.server,share.port)
        
        if not isinstance(share.tmp, int) :
            raise Exceptions.ConnexionFaieled(share)
        
        Sysaccess.mount(share)
        
        if '/tmp/miam/try' in mtab:
            Sysaccess.unmount('/tmp/miam/try')
            return True
        
    def unmount_share(self,share = None):
        if share.automount:
            share.lock_automount = True
        self.threads.launch('UNMOUNT',share)
    
    def unmount_all(self):
        for share in self.shares:
            if share.mounted and share.automount:
                share.lock_auromount = True
                self.threads.launch('UNMOUNT',share)
            
    def open_share(self,share):
        if share.mounted:
            Sysaccess.xdg_open(self.mtab.get_current_mount_point(share))
                
    ### Callbacks
    # Location callbacks
    def add_location_callback(self,locations, location):
        self.get_current_location(location)
    
    def set_location_callback(self,locations, location):
        if self.config.network['CURRENT LOCATION'] == location:
            self.get_current_location()
        else:
            self.get_current_location(location)
            
    def remove_location_callback(self,locations,location_id):
        if location_id == self.config.network['CURRENT LOCATION'].id:
            self.get_current_location()
    
    # Network callbacks
    def remote_addr_changed_callback(self,config, new_addr, old_addr):
        if new_addr:    
            self.get_current_location()
    
    def network_state_callback(self,config, new_state, old_state):
        if new_state:
            self.threads.launch('NETWORK INFO')
            self.threads.start_thread('LATENCY')
            self.threads.start_thread('AUTOMOUNT')
        else:
            self.config.network.load_defaults()         # Fallback network config dict to default
            self.config.network.store['STATE'] = False  ### NASTY >>> Because state default is None. To avoid recall.
            self.threads.stop_thread('LATENCY')
#            self.threads.stop_thread('AUTOMOUNT')
            for share_id,share in self.shares:
                self.shares.set_latency(share,None)
            
    
    def interface_callback(self,config,new_interface,old_interface):
        if old_interface and new_interface:
            self.config.network['STATE'] = False
            self.config.network['STATE'] = True
    
    # Setup Callback
    def set_startup_callback(self,config,new_value,old_value):
        #TODO : implement rc.d scripts
        pass
    
    def hide(self):
        self.threads.stop_thread('LATENCY')
        self.main_window.close()
        
    def show(self):
        self.threads.start_thread('LATENCY')
        self.uimanager = UIManager.UIManager(self)
        self.menubar = self.uimanager.get_widget('/menubar')
        self.toolbar = self.uimanager.get_widget('/toolbar')
        self.main_window = MainWindow.MainWindow(self)
        self.get_current_location()
        self.main_window.set_current_location_callback(self.config,self.config.network['CURRENT LOCATION'],None)
    
    def thread_start(self,threads,thread):
        if thread in threads.started:
            threads.started[thread] = None
    
    def thread_end(self,threads,thread):
        if thread in threads.started:
            threads.started[thread] = threads.time
        
    def main(self):
        '''gtk main loop'''
        gtk.main()
    
    def exit(self):
        '''on exiting'''
        self.threads.stop = True
        if self.config.user['UMOUNT ON EXIT']:
            self.unmount_all()
        
        ### Useless ?
        self.locations.save()
        self.shares.save()
        ###
        if self.main_window in self.windows:
            self.main_window.close()
        gtk.main_quit()
            
if __name__ == "__main__":
    import getopt
    import sys
    try:
        optlist, args = getopt.getopt(sys.argv[1:], '' , ['user_path='])
    except getopt.GetoptError, err:
        print str(err)
    for o,a in optlist:
        if o in('--user_path'):
            controler = Controller(a)
    exit(0)